home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1999 / MacHack 1999.toast / The Hacks / IrComm Remote / DVDRemote / Hack / zzOther / Serial Demo / SerDemo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-06-18  |  18.6 KB  |  689 lines  |  [TEXT/MPS ]

  1.  
  2. #include    <Types.h>
  3. //#include    <SysEqu.h>
  4. #include    <Resources.h>
  5. #include    <QuickDraw.h>
  6. #include    <Fonts.h>
  7. #include    <Events.h>
  8. #include    <Menus.h>
  9. #include    <Controls.h>
  10. #include    <Dialogs.h>
  11. #include    <Memory.h>
  12. #include    <Files.h>
  13. #include    <Devices.h>
  14. #include    <Serial.h>
  15. #include    <Timer.h>
  16. #include    <OSUtils.h>
  17. #include    <GestaltEqu.h>
  18. #include    <Errors.h>
  19. #include    <string.h>
  20.  
  21.  
  22.  
  23. #define        rMonitor        1000
  24. #define        kSendButton        1
  25. #define        kStopButton        2
  26. #define        kQuitButton        3
  27. #define        kMsgBox            4
  28. #define        kSendSpinner    5
  29. #define        kRcvSpinner        7
  30. #define        kHoldFlag        9
  31. #define        kBreakButton    10
  32.  
  33. #define        rSerDataRsrc    128
  34. #define        rSpinnerIcon    1000
  35. #define        rHeldIcon        128
  36. #define        rNotHeldIcon    129
  37.  
  38. #define        rPortOpenALRT    256
  39. #define        kReset            2
  40.  
  41. #define        kCtlEnable        0
  42. #define        kCtlDisable        255
  43. #define        kSerBufSize        16384
  44. #define        kSerRdSize        64
  45. #define        kSerConfig        baud9600 + noParity + data8 + stop10
  46. #define        kBreakLength    666                /* serial break length in milliseconds */
  47.  
  48. enum        {drvrName = 0x12};                /* offset to driver name in 'DRVR' std. header */
  49. enum        {dOpened = 5,
  50.                 dRAMBased = 6,
  51.                 drvrActive = 7};            /* Device Manager DCtlFlag bits */
  52.                 
  53. enum        {kSerStatus = 8,
  54.                 kSerClrBrk = 11,            /* Serial Driver csCodes */
  55.                 kSerSetBrk = 12,
  56.                 kSerHShakeDTR = 14};
  57. enum        {breakR0 = 128};                /* mask for break bit in SCC RR0 -- See TN #56 */
  58. enum        {breakErr = 8};                    /* mask for break bit in cumErrs -- System 7.0 */
  59.  
  60.  
  61.  
  62. typedef struct {
  63.     IOParam        fIOParam;
  64.     long        appA5;
  65. } ExtIOParam;
  66.  
  67. typedef struct {
  68.     TMTask        fTMTask;
  69.     long        appA5;
  70. } ExtTMTask;
  71.  
  72. typedef struct {
  73.     unsigned char    readR0;
  74.     unsigned char    deltaBits;
  75.     short            drvrPosting;
  76. } SERDEventMessage;
  77.  
  78.  
  79.  
  80. Boolean        gHeldOff,
  81.             gAllDone = false,
  82.             gShouldSend = false,
  83.             gReload = false,
  84.             gKillBreak = false,
  85.             gBreakReceived = false;
  86. Ptr            gpSerBuf, *ghSerBuf,
  87.             gpOutputData, *ghOutputData,
  88.             gpBitBucket, *ghBitBucket;
  89. long        gOutputDataSize;
  90. IOParam        *gpSendPB, **ghSendPB;
  91. short        gSysVersion,
  92.             gOutRefNum, gInRefNum,
  93.             gSendCount = 0,
  94.             gRcvCount = 0;
  95. char        *panicString = "Help! I'm stuffed! And here's a bunch of characters to prove it!\n\r";
  96. ExtTMTask    gUnBreakTask;
  97.  
  98.  
  99.  
  100. short    Initialize (void);
  101. void    CleanUp (void);
  102. Boolean    OpenSERD (void);
  103. void    CloseSERD (void);
  104. void    DoIOStuff (void);
  105. OSErr    PrimeSend (void);
  106. void    CheckSerStatus (void);
  107. void    CheckSerData (long reqBytes);
  108. pascal Boolean NullGrabber (DialogPtr, EventRecord *evt, short *itemHit);
  109. pascal void    SendCompRout (void);
  110. pascal void FlagBreakTimeout (void);
  111. OSErr    AssertDrvrOpen (Str255 name, short *refNum);
  112.  
  113. ParmBlkPtr GetParmBlkPtr (void) = 0x2008;        /* MOVE.L A0,D0 */
  114. TMTaskPtr GetTMTaskPtr (void) = 0x2009;            /* MOVE.L A1,D0 */
  115.  
  116. #pragma parameter __D0 PBControlImmed(__A0)
  117. pascal OSErr PBControlImmed(ParmBlkPtr paramBlock) = 0xA204;        /* _Control ,IMMED */
  118.  
  119. #pragma parameter __D0 PBStatusImmed(__A0)
  120. pascal OSErr PBStatusImmed(ParmBlkPtr paramBlock) = 0xA205;            /* _Status ,IMMED */
  121.  
  122.  
  123.  
  124. void main (void)
  125. {
  126.     InitGraf(&qd.thePort);
  127.     InitFonts();
  128.     InitWindows();
  129.     InitMenus();
  130.     InitDialogs(NULL);
  131.     InitCursor();
  132.     
  133.     if (Initialize() == noErr) {
  134.         
  135.         if (OpenSERD()) {    /* open the Serial Driver */
  136.             DoIOStuff();
  137.             CloseSERD();    /* close the Serial Driver */
  138.         }
  139.         CleanUp();
  140.     }
  141.     
  142. }
  143.  
  144. //#define kInputDriver "\p.AIn"
  145. //#define kOutputDriver "\p.AOut"
  146. #define kInputDriver "\p.IrIn"
  147. #define kOutputDriver "\p.IrOut"
  148.  
  149.  
  150. Boolean OpenSERD (void)
  151. {
  152.     OSErr    openOutErr, openInErr;
  153.     OSErr    setBufErr, setCfgErr, setHskErr;
  154.     SerShk    hskFlags;
  155.     long    finalTicks;
  156.     Boolean    takeOverPort = true;
  157.     Boolean    openAOut, openAIn;
  158.     
  159.     openAOut = AssertDrvrOpen(kOutputDriver, &gOutRefNum) == noErr;
  160.     openAIn = AssertDrvrOpen(kInputDriver, &gInRefNum) == noErr;
  161.     if (openAOut || openAIn) {
  162.         if (takeOverPort = CautionAlert(rPortOpenALRT, nil) == kReset) {
  163.             if (openAIn) {
  164.                 KillIO(gInRefNum);
  165.                 CloseDriver(gInRefNum);
  166.             }
  167.             if (openAOut) {
  168.                 KillIO(gOutRefNum);
  169.                 CloseDriver(gOutRefNum);
  170.             }
  171.         }
  172.     }
  173.     
  174.     if (takeOverPort) {
  175.         openOutErr = OpenDriver(kOutputDriver, &gOutRefNum);
  176.         openInErr = OpenDriver(kInputDriver, &gInRefNum);
  177.         
  178.         if (openOutErr == noErr && openInErr == noErr) {
  179.         
  180.             /* There is a bug in the Macintosh IIfx IOP serial driver, in which      */
  181.             /* SerGetBuf() will always return zero characters, even if characters    */
  182.             /* have been received. The bug is exhibited when a remote serial device  */
  183.             /* attempts to send data to the Macintosh IIfx when its serial driver is */
  184.             /* not yet open and it holding off data with hardware handshaking. In    */
  185.             /* such a case, data will flow in immediately when the serial driver     */
  186.             /* opens--quickly filling up the default 64-byte buffer. If the buffer   */
  187.             /* fills up before setting a larger buffer with SerSetBuf(), SerGetBuf() */
  188.             /* "sticks" and stubbornly maintains that there is nothing coming in.    */
  189.             /* At 9600 baud, it takes only a little more than four ticks to fill the */
  190.             /* input buffer. This application demonstrates the bug by virtue of the  */
  191.             /* following delay.                                                      */
  192.             
  193.             Delay(5, &finalTicks);
  194.             
  195.             /* It's always good to first set a non-default input buffer, if desired. */
  196.             /* There is no output buffering, so specify only the input driver.       */
  197.             
  198.             setBufErr = SerSetBuf(gInRefNum, gpSerBuf, kSerBufSize);
  199.             
  200.             hskFlags.fXOn = false;
  201.             hskFlags.fCTS = true;
  202.             hskFlags.xOn = 0x11;
  203.             hskFlags.xOff = 0x13;
  204.             hskFlags.errs = 0;
  205.             
  206.             if (gSysVersion >= 0x0700) {
  207.                 hskFlags.evts = 0;                /* I can use new means of break detection. */
  208.             }
  209.             else {
  210.                 hskFlags.evts = breakEvent;        /* I need the driver to post break events. */
  211.             }
  212.             
  213.             hskFlags.fInX = false;
  214.             hskFlags.fDTR = true;
  215.             
  216.             /* SerHShake() does not support full DTR/CTS hardware handshaking. You   */
  217.             /* accomplish the same thing and more with a Control call and csCode 14. */
  218.             /* You only need to specify hskFlags once, to the output driver.         */
  219.             
  220.             setHskErr = Control(gOutRefNum, kSerHShakeDTR, (Ptr) &hskFlags);
  221.  
  222.             /* Now reset both input and output drivers with the same configuration.  */
  223.             /* Only a single call to the output driver is necessary to do this.      */
  224.             /* Differing concurrent input/output baud rates are not supported.       */
  225.             
  226.             setCfgErr = SerReset(gOutRefNum, kSerConfig);
  227.             
  228.         }
  229.     }
  230.     
  231.     return takeOverPort;
  232. }
  233.  
  234.  
  235.  
  236. void CloseSERD (void)
  237. {
  238.     OSErr    killErr, closeOutErr, closeInErr;
  239.  
  240.     killErr = KillIO(gInRefNum);
  241.     closeInErr = CloseDriver(gInRefNum);
  242.  
  243.     killErr = KillIO(gOutRefNum);
  244.     closeOutErr = CloseDriver(gOutRefNum);
  245.  
  246. }
  247.  
  248.  
  249.  
  250. void CheckSerStatus (void)
  251. {
  252.     OSErr        checkStatErr, panicErr;
  253.     IOParam        altSendPB, *pAltSendPB = &altSendPB;
  254.     CntrlParam    statPB;
  255.     SerStaRec    serStat;
  256.     
  257.     statPB.ioCRefNum = gInRefNum;
  258.     statPB.csCode = kSerStatus;
  259.     checkStatErr = PBStatusImmed((ParmBlkPtr) &statPB);
  260.     serStat =  *(SerStaRec *) &statPB.csParam;
  261.     
  262.     /* I check to see if the remote system has told me to stop sending. */
  263.     
  264.     gHeldOff = (Boolean) serStat.ctsHold;
  265.     
  266.     /* Check for errors. */
  267.     
  268.     if (serStat.cumErrs & swOverrunErr)
  269.         DebugStr("\pSoftware Overrun Error");
  270.     else if (serStat.cumErrs & parityErr)
  271.         DebugStr("\pParity Error");
  272.     else if (serStat.cumErrs & hwOverrunErr)
  273.         DebugStr("\pHardware Overrun Error");
  274.     else if (serStat.cumErrs & framingErr)
  275.         DebugStr("\pFraming Error");
  276.     
  277.     /* If I have System 7.0 or better, I can check directly to see if   */
  278.     /* I've received a break. Usually I don't check for a feature this  */
  279.     /* way, but in this case I have no alternative.                     */
  280.     
  281.     if (gSysVersion >= 0x0700) {
  282.         gBreakReceived = (serStat.cumErrs & breakErr) != 0;
  283.     }
  284.     
  285.     /* All I do here is send a small "panic" packet of characters back  */
  286.     /* to the remote system when it fills _my_ buffer. I don't actually */
  287.     /* know the exact state of my buffer, but I can see if I've told    */
  288.     /* the remote system to shut up, indicating that I'm mostly full.   */
  289.     
  290.     if (checkStatErr == noErr) {
  291.         if (( (unsigned char) serStat.xOffSent & dtrNegated) != 0) {
  292.             pAltSendPB->ioCompletion = NULL;
  293.             pAltSendPB->ioRefNum = gOutRefNum;
  294.             pAltSendPB->ioBuffer = panicString;
  295.             pAltSendPB->ioReqCount = strlen(panicString);
  296.             PBWriteAsync((ParmBlkPtr) pAltSendPB);
  297.             
  298.             /* The program may hang here if the user quits the    */
  299.             /* remote application first--that could hold off our  */
  300.             /* serial output, leaving a pending asynchronous I/O  */
  301.             /* request and keeping us in an infinite loop.        */
  302.             
  303.             while (pAltSendPB->ioResult > 0) {}                /* I'll fix it later. */
  304.             /* The reason I do this instead of just calling it synchronously */
  305.             /* is so that if I do hang, I'll hang in my code for an obvious  */
  306.             /* reason instead of hanging up in the Device Manager.           */
  307.             panicErr = pAltSendPB->ioResult;
  308.         }
  309.     }
  310. }
  311.             
  312.  
  313.  
  314. void CheckSerData (long reqBytes)
  315. {
  316.     OSErr    checkBufErr, serRdErr;
  317.     long    charCount;
  318.     
  319.     // long    finalTicks;
  320.     // register long overrun;
  321.     
  322.     checkBufErr = SerGetBuf(gInRefNum, &charCount);
  323.     if (checkBufErr == noErr) {
  324.     
  325.         /* The general strategy here is this: if number of available characters */
  326.         /* meets a certain minimum threshold, then I read in everything in the  */
  327.         /* buffer. If I get delayed, I'll catch up quickly.                     */
  328.  
  329.         if (charCount != 0 && charCount >= reqBytes) {
  330.             /*
  331.             overrun = charCount;
  332.             Control(gOutRefNum, 18, nil);
  333.             Delay(300, &finalTicks);
  334.             Control(gOutRefNum, 17, nil);
  335.             SerGetBuf(gInRefNum, &charCount);
  336.             overrun = charCount - overrun;
  337.             DebugStr("\pSender Halted");
  338.             */
  339.             serRdErr = FSRead(gInRefNum, &charCount, gpBitBucket);
  340.             if (serRdErr == noErr) {
  341.                 gRcvCount++;            /* increment a counter for the input spinner */
  342.             }
  343.         }
  344.     }
  345. }
  346.  
  347.  
  348.  
  349. void DoIOStuff (void)
  350. {
  351.     DialogPtr        serMonitor;
  352.     OSErr            primeErr;
  353.     short            itemHit, itemType;
  354.     ControlHandle    sendItem, stopItem, breakItem;
  355.     Handle            spinner, flag, item;
  356.     Rect            box;
  357.     CntrlParam        breakPB;
  358.     
  359.     serMonitor = GetNewDialog(rMonitor, NULL, NULL);
  360.     SetPort((GrafPtr) serMonitor);
  361.             
  362.     while (!gAllDone) {
  363.         
  364.             CheckSerStatus();
  365.         
  366.         /* Holding down the mouse button prevents checking the input buffer     */
  367.         /* and forces the input buffer to fill up. This allows experimentation. */
  368.             
  369.             if (!Button()) {
  370.                 CheckSerData(kSerRdSize);
  371.             }
  372.     
  373.             if (gShouldSend && gReload) {
  374.                 gSendCount++;                /* increment a counter for the output spinner */
  375.                 gReload = !gReload;
  376.             }
  377.             
  378.         /* The break timer simply sets a global flag which I use to indicate when  */
  379.         /* to clear a break condition. Again, I use an immediate Control call, but */
  380.         /* primarily for consistency, and also to show off.                        */
  381.             
  382.             if (gKillBreak) {
  383.                 breakPB.ioCRefNum = gOutRefNum;
  384.                 breakPB.csCode = kSerClrBrk;
  385.                 PBControlImmed((ParmBlkPtr) &breakPB);        /* SerClrBrk(), but IMMED */
  386.                 gKillBreak = !gKillBreak;
  387.                 GetDItem(serMonitor, kBreakButton, &itemType, &(Handle) breakItem, &box);
  388.                 HiliteControl(breakItem, kCtlEnable);
  389.             }
  390.         
  391.         /* If another area of the program detects a break, I flag the occurrence here. */
  392.         
  393.             if (gBreakReceived) {
  394.                 SysBeep(1);
  395.                 SysBeep(1);
  396.                 gBreakReceived = !gBreakReceived;
  397.             }
  398.         
  399.         /* Here I update all the buttons and icons. I probably spend too much time    */
  400.         /* doing this when it isn't necessary, but that's not the point.              */
  401.         
  402.         /* I animate the beach balls by changing the resource IDs of the ICONs in the */
  403.         /* DITL and invalidating their enclosing rectangles. I also hilight controls  */
  404.         /* appropriately and display annunciators when necessary.                     */
  405.             
  406.             GetDItem(serMonitor, kSendButton, &itemType, &(Handle) sendItem, &box);
  407.             GetDItem(serMonitor, kStopButton, &itemType, &(Handle) stopItem, &box);
  408.             if (gShouldSend) {
  409.                 HiliteControl(sendItem, kCtlDisable);
  410.                 HiliteControl(stopItem, kCtlEnable);
  411.             }
  412.             else {
  413.                 HiliteControl(sendItem, kCtlEnable);
  414.                 HiliteControl(stopItem, kCtlDisable);
  415.             }
  416.             
  417.             spinner = Get1Resource('ICON', rSpinnerIcon + gSendCount % 8);
  418.             GetDItem(serMonitor, kSendSpinner, &itemType, &item, &box);
  419.             SetDItem(serMonitor, kSendSpinner, iconItem, spinner, &box);
  420.             InvalRect(&box);
  421.             
  422.             spinner = Get1Resource('ICON', rSpinnerIcon + gRcvCount % 8);
  423.             GetDItem(serMonitor, kRcvSpinner, &itemType, &item, &box);
  424.             SetDItem(serMonitor, kRcvSpinner, iconItem, spinner, &box);
  425.             InvalRect(&box);
  426.             
  427.             if (gShouldSend && gHeldOff) {
  428.                 flag = Get1Resource('ICON', rHeldIcon);
  429.             }
  430.             else {
  431.                 flag = Get1Resource('ICON', rNotHeldIcon);
  432.             }
  433.             GetDItem(serMonitor, kHoldFlag, &itemType, &item, &box);
  434.             SetDItem(serMonitor, kHoldFlag, iconItem, flag, &box);
  435.             InvalRect(&box);
  436.                 
  437.         /* In lieu of an event loop, I just use a modal dialog with a relatively  */
  438.         /* simple (but unusual) filterProc. This is not a good example of how to  */
  439.         /* write an app. Modal dialogs are evil and to be avoided if possible.    */
  440.         /* Nonetheless, the filterProc is an interesting example unto itself....  */
  441.     
  442.             ModalDialog(NullGrabber, &itemHit);
  443.             switch (itemHit) {
  444.                 
  445.                 case kStopButton:
  446.                     if (gShouldSend) {
  447.                         gShouldSend = !gShouldSend;
  448.                     }
  449.                     break;
  450.                 
  451.                 case kSendButton:
  452.                     if (!gShouldSend) {
  453.                         gShouldSend = !gShouldSend;
  454.                         primeErr = PrimeSend();
  455.                     }
  456.                     break;
  457.                 
  458.                 case kBreakButton:
  459.                 
  460.                 /* In another possible Mac IIfx IOP bug, SerSetBrk() called synchronously  */
  461.                 /* appears to hang the machine if an async write is pending. Since that is */
  462.                 /* often the case (at least in this application) I work around the problem */
  463.                 /* by making the Control call immediate--this prevents the hang, but also  */
  464.                 /* raises another interesting issue about when break is actually asserted. */
  465.                 
  466.                     breakPB.ioCRefNum = gOutRefNum;
  467.                     breakPB.csCode = kSerSetBrk;
  468.                     PBControlImmed((ParmBlkPtr) &breakPB);        /* SerSetBrk(), but IMMED */
  469.                     
  470.                 /* With break asserted, I prime a Time Manager task to flag the end of    */
  471.                 /* the break, and disable the Break button so that it cannot be selected  */
  472.                 /* again until break is negated.                                          */
  473.                 
  474.                     PrimeTime((QElemPtr) &gUnBreakTask, kBreakLength);
  475.                     GetDItem(serMonitor, kBreakButton, &itemType, &(Handle) breakItem, &box);
  476.                     HiliteControl(breakItem, kCtlDisable);
  477.                     break;
  478.                 
  479.                 case kQuitButton:
  480.                     gAllDone = true;
  481.                     break;
  482.                 
  483.             }
  484.     }
  485.     
  486.     DisposDialog(serMonitor);
  487.     
  488. }
  489.  
  490.  
  491.  
  492. pascal Boolean NullGrabber (DialogPtr, EventRecord *evt, short *itemHit)
  493. {
  494.     EventRecord    driverEvent;
  495.     
  496.     /* Without this filterProc, none of the animation works: */
  497.     
  498.     /* In order to keep things rolling along even when there are no events    */
  499.     /* such as mouse clicks or keystrokes, I have to return true in response  */
  500.     /* to null events. This is unusual, but otherwise ModalDialog() "handles" */
  501.     /* null events by eating them and waiting for something better. This is   */
  502.     /* bad if I need to turn the spinner or clear a break condition.          */
  503.     
  504.     /* Also, I check for driver events here in order to detect breaks. */
  505.     
  506.     if (GetNextEvent(driverMask, &driverEvent)) {
  507.         if ((*(SERDEventMessage *) &driverEvent.message).drvrPosting == gInRefNum) {
  508.             gBreakReceived = ((*(SERDEventMessage *) &driverEvent.message).readR0 & breakR0) != 0;
  509.         }
  510.     }
  511.     
  512.     if (evt->what == nullEvent) {
  513.         *itemHit = 0;
  514.         return true;
  515.     }
  516.     
  517.     else {
  518.         return false;
  519.     }
  520.     
  521. }
  522.  
  523.  
  524.  
  525. pascal void SendCompRout (void)
  526. {
  527.     long    oldA5;
  528.     
  529.     oldA5 = SetA5(((ExtIOParam *) GetParmBlkPtr())->appA5);        /* retrieve A5 */
  530.     
  531.     if (gShouldSend && !gAllDone) {
  532.         gReload = true;
  533.         gpSendPB->ioCompletion = (ProcPtr) SendCompRout;
  534.         PBWriteAsync((ParmBlkPtr) gpSendPB);            /* this is the self-sustaining part */        
  535.     }
  536.     
  537.     SetA5(oldA5);
  538.     
  539. }
  540.  
  541.  
  542.  
  543. OSErr PrimeSend (void)
  544. {
  545.     gpSendPB->ioCompletion = (ProcPtr) SendCompRout;
  546.     gpSendPB->ioRefNum = gOutRefNum;
  547.     gpSendPB->ioBuffer = gpOutputData;
  548.     gpSendPB->ioReqCount = gOutputDataSize;
  549.     ((ExtIOParam *) gpSendPB)->appA5 = SetCurrentA5();    /* completion routine needs A5 */
  550.     gReload = true;
  551.     
  552.     return PBWriteAsync((ParmBlkPtr) gpSendPB);            /* asynchronous self-sustaining sends */
  553.  
  554. }
  555.  
  556.  
  557.  
  558. pascal void FlagBreakTimeout (void)
  559. {
  560.     long    oldA5;
  561.     
  562.     oldA5 = SetA5(((ExtTMTask *) GetTMTaskPtr())->appA5);        /* retrieve A5 */
  563.     
  564.     gKillBreak = true;
  565.     
  566.     SetA5(oldA5);
  567.     
  568. }
  569.  
  570.  
  571.  
  572. OSErr AssertDrvrOpen (Str255 name, short *refNum)
  573. {
  574.     DCtlHandle    *pUTEntry;
  575.     Ptr            pDrvr;
  576.     OSErr        result = notOpenErr;        /* assume not open */
  577.     short        unitNo;
  578.     char        *aDrvrName;
  579.     
  580.     /* The point here is to determine whether a driver is open, given its name.  */
  581.     /* This allows one to check a driver to see if it's open without hard coding */
  582.     /* its reference number. (Normally, the way to get the refNum is to open     */
  583.     /* the driver--but that defeats the whole purpose!)                          */
  584.     /* This is an extension of the code discussed in Tech Note #71.              */
  585.     
  586.     *refNum = 0;
  587.     pUTEntry = *(DCtlHandle **) UTableBase;
  588.     for (unitNo = 0; unitNo < *(short *) UnitNtryCnt; unitNo++, pUTEntry++) {
  589.         if (*pUTEntry != nil && **pUTEntry != nil) {
  590.             if (((***pUTEntry).dCtlFlags & 1 << dRAMBased) != 0)
  591.                 pDrvr = *(Handle) (***pUTEntry).dCtlDriver;
  592.             else
  593.                 pDrvr = (***pUTEntry).dCtlDriver;
  594.             
  595.             if (pDrvr != nil) {
  596.                 aDrvrName = pDrvr + drvrName;
  597.                 if (memcmp(aDrvrName, name, 1 + name[0]) == 0) {
  598.                     /* We found the one we're after. */
  599.                     *refNum = ~unitNo;
  600.                     if (((***pUTEntry).dCtlFlags & 1 << dOpened) != 0)
  601.                         result = noErr;
  602.                     break;
  603.                 }
  604.             }
  605.         }
  606.     }    
  607.     
  608.     return result;
  609. }
  610.  
  611.  
  612.  
  613. short Initialize (void)
  614. {
  615.     long    result;
  616.     OSErr    gestErr;
  617.     
  618.     gestErr = Gestalt(gestaltSystemVersion, &result);
  619.     gSysVersion = result;
  620.     
  621.     ghSendPB = (IOParam **) NewHandle( sizeof(ExtIOParam));
  622.     if (ghSendPB != NULL) {
  623.         MoveHHi((Handle) ghSendPB);
  624.         HLock((Handle) ghSendPB);
  625.         gpSendPB = *ghSendPB;
  626.     }
  627.     else {
  628.         return MemError();
  629.     }
  630.  
  631.     ghSerBuf = NewHandle(kSerBufSize);
  632.     if (ghSerBuf != NULL) {
  633.         MoveHHi((Handle) ghSerBuf);
  634.         HLock((Handle) ghSerBuf);
  635.         gpSerBuf = *ghSerBuf;
  636.     }
  637.     else {
  638.         DisposHandle((Handle) ghSendPB);
  639.         return MemError();
  640.     }
  641.     
  642.     ghBitBucket = NewHandle(kSerBufSize);
  643.     if (ghBitBucket != NULL) {
  644.         MoveHHi(ghBitBucket);
  645.         HLock(ghBitBucket);
  646.         gpBitBucket = *ghBitBucket;
  647.     }
  648.     else {
  649.         DisposHandle((Handle) ghSendPB);
  650.         DisposHandle((Handle) ghSerBuf);
  651.         return MemError();
  652.     }
  653.     
  654.     ghOutputData = Get1Resource('sDAT', rSerDataRsrc);
  655.     if (ghOutputData != NULL) {
  656.         MoveHHi(ghOutputData);
  657.         HLock(ghOutputData);
  658.         gpOutputData = *ghOutputData;
  659.         gOutputDataSize = SizeResource(ghOutputData);
  660.     }
  661.     else {
  662.         DisposHandle((Handle) ghSendPB);
  663.         DisposHandle((Handle) ghSerBuf);
  664.         DisposHandle(ghBitBucket);
  665.         return ResError();
  666.     }
  667.     
  668.     /* I use a Time Manager task to time breaks. This is where I install it. */
  669.     
  670.     gUnBreakTask.fTMTask.tmAddr = FlagBreakTimeout;
  671.     gUnBreakTask.appA5 = SetCurrentA5();
  672.     InsTime((QElemPtr) &gUnBreakTask);
  673.     
  674.     return noErr;
  675. }
  676.  
  677.  
  678.  
  679. void CleanUp (void)
  680. {
  681.     /* I remove the Time Manager task before exiting. */
  682.     
  683.     RmvTime((QElemPtr) &gUnBreakTask);
  684.  
  685.     DisposHandle((Handle) ghSendPB);
  686.     DisposHandle(ghSerBuf);
  687.     DisposHandle(ghBitBucket);
  688. }
  689.